package fluidmail;

import java.io.File;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

/**
 * Fluid API for sending mail.
 * 
 * @author Victor Hugo Herrera Maldonado
 */
public class Email {
    private List<File> attachedFiles = new LinkedList<File>();
    private String content;
    private String contentType;
    private MimeMessage message;

    /* No public constructor. */
    private Email(MimeMessage message) {
        this.message = message;
    }

    
    /**
     * Creates an instance of Email.
     * 
     * @param session The session to create the email.
     * @return Email instance.
     */
    public static Email create(Session session) {
        return new Email(new MimeMessage(session));
    }
    
    /**
     * Sets the from header for the email.
     * 
     * @param address
     * @return This Email instance.
     * @throws MessagingException 
     */
    public Email setFrom(String address) throws MessagingException {
        message.setFrom(new InternetAddress(address));
        return this;
    }
    
    /**
     * Sets the subject of the email.
     * 
     * @param subject
     * @return This Email instance.
     * @throws MessagingException 
     */
    public Email setSubject(String subject) throws MessagingException {
        message.setSubject(subject);
        return this;
    }
    
    /**
     * Sets a header of the email.
     * 
     * @param name
     * @param value
     * @return This Email instance.
     * @throws MessagingException 
     */
    public Email setHeader(String name, String value) throws MessagingException{
        message.setHeader(name, value);
        return this;
    }

    /**
     * Add recipient addresses with type TO.
     * 
     * @param addresses
     * @return
     * @throws AddressException
     * @throws MessagingException 
     */
    public Email addTo(String... addresses) throws AddressException, MessagingException {
        for(String address:addresses){
            message.addRecipient(RecipientType.TO, new InternetAddress(address));
        }
        return this;
    }

    /**
     * Add recipient addresses with type CC.
     * 
     * @param addresses
     * @return This Email instance.
     * @throws AddressException
     * @throws MessagingException 
     */
    public Email addCC(String... addresses) throws AddressException, MessagingException {
        for(String address:addresses){
            message.addRecipient(RecipientType.CC, new InternetAddress(address));
        }
        return this;
    }
    
    /**
     * Add recipient addresses with type BCC.
     * 
     * @param addresses
     * @return This Email instance.
     * @throws AddressException
     * @throws MessagingException 
     */
    public Email addBCC(String... addresses) throws AddressException, MessagingException {
        for(String address:addresses){
            message.addRecipient(RecipientType.BCC, new InternetAddress(address));
        }
        return this;
    }
    
    /**
     * Add addresses to replyTo header.
     * 
     * @param addresses
     * @return This Email instance.
     * @throws AddressException
     * @throws MessagingException 
     */
    public Email addReplyTo(String... addresses) throws AddressException, MessagingException{
        Address[] replyTo = message.getReplyTo();
        List<Address> list=new LinkedList<Address>();
        if(replyTo != null){
            list.addAll(Arrays.asList(replyTo));
        }
        for(String a:addresses){
            list.add(new InternetAddress(a));
        }
        message.setReplyTo(list.toArray(new InternetAddress[0]));
        return this;
    }

    /**
     * Sets the content of the email. Content type is set to "text/html".
     * 
     * @param htmlContent
     * @return This Email instance.
     */
    public Email setHTMLContent(String htmlContent) {
        content=htmlContent;
        contentType="text/html";
        return this;
    }

    /**
     * Sets the content of the email. Content type is set to "text/plain".
     * 
     * @param htmlContent
     * @return This Email instance.
     */
    public Email setPlainContent(String plainContent) {
        content=plainContent;
        contentType="text/plain";
        return this;
    }

    /**
     * Attach a file to the email.
     * <p>
     * Note: The file is not inmediately attached. The file is added to a list of references to attach.
     * The attaching of the file content is made when the email is sent, so be careful of content modifications of the file after
     * invoking this method.
     * </p>
     * @param file The file to attach.
     * @return This Email instance.
     */
    public Email attach(File file) {
        attachedFiles.add(file);
        return this;
    }

    /**
     * Send the email.
     * 
     * @return
     * @throws MessagingException 
     */
    public Email send() throws MessagingException {
        if( (content == null || content.trim().equals("")) && attachedFiles.isEmpty()){
            throw new IllegalStateException("Neither content or attached files to send");
        }
        if ( !attachedFiles.isEmpty() ) {
            Multipart multipart = new MimeMultipart();

            /* Set main content */
            if (content != null) {
                MimeBodyPart part = new MimeBodyPart();
                part.setContent(content, contentType);
                multipart.addBodyPart(part);
            }

            /* Attach files */
            for (File file : attachedFiles) {
                MimeBodyPart part = new MimeBodyPart();
                DataSource dataSource = new FileDataSource(file);
                part.setDataHandler(new DataHandler(dataSource));
                part.setFileName(file.getName());
                multipart.addBodyPart(part);
            }
            message.setContent(multipart);
        } else {
            message.setContent(content, contentType);
        }
        Transport.send(message);
        return this;
    }
    
}
